bitkeeper revision 1.567 (3fa98625j0d47oe7ZCKLDdbnYED8wA)
authorkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Wed, 5 Nov 2003 23:22:13 +0000 (23:22 +0000)
committerkaf24@scramble.cl.cam.ac.uk <kaf24@scramble.cl.cam.ac.uk>
Wed, 5 Nov 2003 23:22:13 +0000 (23:22 +0000)
xi_restore_linux.c, xi_save_linux.c:
  new file
dom0_ops.c, Makefile:
  Starting DOM0 support for suspend/resume. Untested so far, and resume is currently incomplete.

.rootkeys
tools/internal/Makefile
tools/internal/xi_restore_linux.c [new file with mode: 0644]
tools/internal/xi_save_linux.c [new file with mode: 0644]
xen/common/dom0_ops.c

index 5d4c02c8f707ddd04a0ee6deb875ee31a62bc567..f12c77063a4af27abf9d387fc89ea726c90744fa 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 3ec43c5dmQxGDvgJJXbV1yLxT30Y1A tools/internal/xi_helper
 3f108ad5wQm0ZaQ4GXFoUhH1W1aW9w tools/internal/xi_list.c
 3f0458aaXhD8BQAggO81gv30RQ-ifA tools/internal/xi_phys_grant.c
+3fa9861aBdNV1yCjfY4cLPr4Mtrpuw tools/internal/xi_restore_linux.c
+3fa98615LWZfagwDBp7XfuC-u9wi3w tools/internal/xi_save_linux.c
 3f108adb2b5OkKL6-faG3lMiOYDf_w tools/internal/xi_sched_domain.c
 3f108ade1v8weyh1sKx890VTd240Hw tools/internal/xi_sched_global.c
 3eb781fd8oRfPgH7qTh7xvgmwD6NgA tools/internal/xi_start.c
index c8b8dcffaf61a0243214acc65f1eb7895b48b5ac..83e23d2aa3801d806a487639549cef701facb776 100644 (file)
@@ -8,7 +8,7 @@ SRCS     = $(wildcard *.c)
 OBJS     = $(patsubst %.c,%.o,$(SRCS))
 
 TARGETS  = xi_create xi_start xi_stop xi_destroy xi_build 
-TARGETS += xi_phys_grant xi_list 
+TARGETS += xi_phys_grant xi_list xi_save_linux xi_restore_linux
 TARGETS += xi_sched_global xi_sched_domain xi_usage xi_vif_params
 INSTALL  = $(TARGETS) xi_vifinit xi_helper
 
diff --git a/tools/internal/xi_restore_linux.c b/tools/internal/xi_restore_linux.c
new file mode 100644 (file)
index 0000000..90ec98a
--- /dev/null
@@ -0,0 +1,346 @@
+/******************************************************************************
+ * xi_restore_linux.c
+ * 
+ * Restore the state of a Xenolinux session.
+ * 
+ * Copyright (c) 2003, K A Fraser.
+ */
+
+#include "dom0_defs.h"
+#include "mem_defs.h"
+#include <asm-xeno/suspend.h>
+
+static char *argv0 = "internal_save_linux";
+
+/* A table mapping each PFN to its current MFN. */
+static unsigned long *pfn_to_mfn_table;
+/* A table mapping each current MFN to its canonical PFN. */
+static unsigned long *mfn_to_pfn_table;
+
+static int devmem_fd;
+
+static int init_pfn_mapper(void)
+{
+    if ( (devmem_fd = open("/dev/mem", O_RDWR)) < 0 )
+    {
+        PERROR("Could not open /dev/mem");
+        return -1;
+    }
+    return 0;
+}
+
+static void *map_pfn(unsigned long pfn)
+{
+    void *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
+                       MAP_SHARED, devmem_fd, pfn << PAGE_SHIFT);
+    if ( vaddr == MAP_FAILED )
+    {
+        PERROR("Could not mmap a domain pfn using /dev/mem");
+        return NULL;
+    }
+    return vaddr;
+}
+
+static void unmap_pfn(void *vaddr)
+{
+    (void)munmap(vaddr, PAGE_SIZE);
+}
+
+/*
+ * Returns TRUE if the given machine frame number has a unique mapping
+ * in the guest's pseudophysical map.
+ */
+#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \
+    (((_mfn) < (1024*1024)) &&          \
+     (pfn_to_mfn_table[mfn_to_pfn_table[_mfn]] == (_mfn)))
+
+/* Returns TRUE if MFN is successfully converted to a PFN. */
+static int translate_mfn_to_pfn(unsigned long *pmfn)
+{
+    unsigned long mfn = *pmfn;
+    if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
+        return 0;
+    *pmfn = mfn_to_pfn_table[mfn];
+    return 1;
+}
+
+static int check_pfn_ownership(unsigned long mfn, unsigned int dom)
+{
+    dom0_op_t op;
+    op.cmd = DOM0_GETPAGEFRAMEINFO;
+    op.u.getpageframeinfo.pfn = mfn;
+    if ( (do_dom0_op(&op) < 0) || (op.u.getpageframeinfo.domain != dom) )
+        return 0;
+    return 1;
+}
+
+static unsigned int get_pfn_type(unsigned long mfn)
+{
+    dom0_op_t op;
+    op.cmd = DOM0_GETPAGEFRAMEINFO;
+    op.u.getpageframeinfo.pfn = mfn;
+    if ( do_dom0_op(&op) < 0 )
+    {
+        PERROR("Unexpected failure when getting page frame info!");
+        exit(1);
+    }
+    return op.u.getpageframeinfo.type;
+}
+
+static int checked_read(int fd, void *buf, size_t count)
+{
+    int rc;
+    while ( ((rc = read(fd, buf, count)) == -1) && (errno == EINTR) )
+        continue;
+    return rc == count;
+}
+
+int main(int argc, char **argv)
+{
+    dom0_op_t op;
+    int rc = 1, i;
+    unsigned long mfn, dom = 0;
+    
+    /* Number of page frames in use by this XenoLinux session. */
+    unsigned long nr_pfns;
+    
+    /* A copy of the CPU context of the guest. */
+    full_execution_context_t ctxt;
+
+    /* First 16 bytes of the state file must contain 'XenoLinuxSuspend'. */
+    char signature[16];
+    
+    /* A copy of the domain's name. */
+    char name[MAX_DOMAIN_NAME];
+
+    /* A table containg the type of each PFN (/not/ MFN!). */
+    unsigned long *pfn_type;
+
+    /* A temporary mapping, and a copy, of one frame of guest memory. */
+    unsigned long *ppage, page[1024];
+
+    /* A copy of the pfn-to-mfn table frame list. */
+    unsigned long pfn_to_mfn_frame_list[1024];
+    /* A temporary mapping of one frame in the above list. */
+    unsigned long *pfn_to_mfn_frame;
+
+    /* A temporary mapping, and a copy, of the guest's suspend record. */
+    suspend_record_t *p_srec, srec;
+
+    /* The name and descriptor of the file that we are reading from. */
+    char *filename;
+    int fd;
+
+    if ( argv[0] != NULL ) 
+        argv0 = argv[0];
+
+    if ( argc != 2 )
+    {
+        fprintf(stderr, "Usage: %s <state file>\n", argv0);
+        return 1;
+    }
+
+    filename = argv[1];
+    if ( (fd = open(name, O_RDONLY)) == -1 )
+    {
+        PERROR("Could not open file for writing");
+        return 1;
+    }
+
+    /* Start writing out the saved-domain record. */
+    if ( !checked_read(fd, signature, 16) ||
+         (memcmp(signature, "XenoLinuxSuspend", 16) != 0) )
+    {
+        ERROR("Unrecognised state format -- no signature found");
+        goto out;
+    }
+
+    if ( !checked_read(fd, name,                  sizeof(name)) ||
+         !checked_read(fd, &nr_pfns,              sizeof(unsigned long)) ||
+         !checked_read(fd, &ctxt,                 sizeof(ctxt)) ||
+         !checked_read(fd, pfn_to_mfn_frame_list, PAGE_SIZE) )
+    {
+        ERROR("Error when reading from state file");
+        goto out;
+    }
+
+    if ( nr_pfns > 1024*1024 )
+    {
+        ERROR("Invalid state file -- pfn count out of range");
+        goto out;
+    }
+
+    for ( i = 0; i < MAX_DOMAIN_NAME; i++ )
+    {
+        if ( name[i] == '\0' ) break;
+        if ( name[i] & 0x80 )
+        {
+            ERROR("Random characters in domain name");
+            goto out;
+        }
+    }
+    name[MAX_DOMAIN_NAME-1] = '\0';
+
+    /* We want zeroed memory so use calloc rather than malloc. */
+    mfn_to_pfn_table = calloc(1, 4 * 1024 * 1024);
+    pfn_to_mfn_table = calloc(1, 4 * nr_pfns);
+    pfn_type         = calloc(1, 4 * nr_pfns);    
+
+    if ( !checked_read(fd, pfn_type, 4 * nr_pfns) )
+    {
+        ERROR("Error when reading from state file");
+        goto out;
+    }
+
+    /* Create a new domain of teh appropriate size, and find it's dom_id. */
+    op.cmd = DOM0_CREATEDOMAIN;
+    op.u.createdomain.memory_kb = nr_pfns * (PAGE_SIZE / 1024);
+    memcpy(op.u.createdomain.name, name, MAX_DOMAIN_NAME);
+    if ( do_dom0_op(&op) < 0 )
+    {
+        ERROR("Could not create new domain");
+        goto out;
+    }
+    dom = op.u.createdomain.domain;
+
+    if ( init_pfn_mapper() < 0 )
+        goto out;
+
+    /* Is the suspend-record MFN actually valid for this domain? */
+    if ( !check_pfn_ownership(ctxt.i386_ctxt.esi, dom) )
+    {
+        ERROR("Invalid state record pointer");
+        goto out;
+    }
+
+    /* If the suspend-record MFN is okay then grab a copy of it to @srec. */
+    p_srec = map_pfn(ctxt.i386_ctxt.esi);
+    memcpy(&srec, p_srec, sizeof(srec));
+    unmap_pfn(p_srec);
+
+    if ( !check_pfn_ownership(srec.pfn_to_mfn_frame_list, dom) )
+    {
+        ERROR("Invalid pfn-to-mfn frame list pointer");
+        goto out;
+    }
+
+    /*
+     * Construct the local pfn-to-mfn and mfn-to-pfn tables. On exit from this
+     * loop we have each MFN mapped at most once. Note that there may be MFNs
+     * that aren't mapped at all: we detect these by MFN_IS_IN_PSEUDOPHYS_MAP.
+     */
+    pfn_to_mfn_frame = NULL;
+    for ( i = 0; i < srec.nr_pfns; i++ )
+    {
+        /* Each frameful of table frames must be checked & mapped on demand. */
+        if ( (i & 1023) == 0 )
+        {
+            mfn = pfn_to_mfn_frame_list[i/1024];
+            if ( !check_pfn_ownership(mfn, dom) )
+            {
+                ERROR("Invalid frame number if pfn-to-mfn frame list");
+                goto out;
+            }
+            if ( pfn_to_mfn_frame != NULL )
+                unmap_pfn(pfn_to_mfn_frame);
+            pfn_to_mfn_frame = map_pfn(mfn);
+        }
+        
+        mfn = pfn_to_mfn_frame[i & 1023];
+
+        if ( !check_pfn_ownership(mfn, dom) )
+        {
+            ERROR("Invalid frame specified with pfn-to-mfn table");
+            goto out;
+        }
+
+        pfn_to_mfn_table[i] = mfn;
+
+        /* Did we map this MFN already? That would be invalid! */
+        if ( MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
+        {
+            ERROR("A machine frame appears twice in pseudophys space");
+            goto out;
+        }
+        
+        mfn_to_pfn_table[mfn] = i;
+
+        /* Query page type by MFN, but store it by PFN. */
+        pfn_type[i] = get_pfn_type(mfn);
+    }
+
+    /* Canonicalise the suspend-record frame number. */
+    if ( !translate_mfn_to_pfn(&ctxt.i386_ctxt.esi) )
+    {
+        ERROR("State record is not in range of pseudophys map");
+        goto out;
+    }
+
+    /* Canonicalise each GDT frame number. */
+    for ( i = 0; i < ctxt.gdt_ents; i += 512 )
+    {
+        if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) )
+        {
+            ERROR("GDT frame is not in range of pseudophys map");
+            goto out;
+        }
+    }
+
+    /* Canonicalise the page table base pointer. */
+    if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) )
+    {
+        ERROR("PT base is not in range of pseudophys map");
+        goto out;
+    }
+    ctxt.pt_base = mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] << PAGE_SHIFT;
+
+    /* Canonicalise the pfn-to-mfn table frame-number list. */
+    for ( i = 0; i < srec.nr_pfns; i += 1024 )
+    {
+        if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) )
+        {
+            ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys");
+            goto out;
+        }
+    }
+
+    /* Now write out each data page, canonicalising page tables as we go... */
+    for ( i = 0; i < srec.nr_pfns; i++ )
+    {
+        mfn = pfn_to_mfn_table[i];
+        ppage = map_pfn(mfn);
+        memcpy(&page, ppage, PAGE_SIZE);
+        unmap_pfn(ppage);
+        if ( (pfn_type[i] == L1TAB) || (pfn_type[i] == L2TAB) )
+        {
+            for ( i = 0; i < 1024; i++ )
+            {
+                if ( !(page[i] & _PAGE_PRESENT) ) continue;
+                mfn = page[i] >> PAGE_SHIFT;
+                if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
+                {
+                    ERROR("Frame number in pagetable page is invalid");
+                    goto out;
+                }
+                page[i] &= PAGE_SIZE - 1;
+                page[i] |= mfn_to_pfn_table[mfn] << PAGE_SHIFT;
+            }
+        }
+        write(fd, &page, PAGE_SIZE);
+    }
+
+    /* Success! */
+    rc = 0;
+
+ out:
+    /* If we experience an error then kill the half-constructed domain. */
+    if ( (rc != 0) && (dom != 0) )
+    {
+        op.cmd = DOM0_DESTROYDOMAIN;
+        op.u.destroydomain.domain = dom;
+        op.u.destroydomain.force  = 1;
+        (void)do_dom0_op(&op);
+    }
+
+    return !!rc;
+}
diff --git a/tools/internal/xi_save_linux.c b/tools/internal/xi_save_linux.c
new file mode 100644 (file)
index 0000000..5162ae9
--- /dev/null
@@ -0,0 +1,365 @@
+/******************************************************************************
+ * xi_save_linux.c
+ * 
+ * Save the state of a running Xenolinux session.
+ * 
+ * Copyright (c) 2003, K A Fraser.
+ */
+
+#include "dom0_defs.h"
+#include "mem_defs.h"
+#include <asm-xeno/suspend.h>
+
+static char *argv0 = "internal_save_linux";
+
+/* A table mapping each PFN to its current MFN. */
+static unsigned long *pfn_to_mfn_table;
+/* A table mapping each current MFN to its canonical PFN. */
+static unsigned long *mfn_to_pfn_table;
+
+static int devmem_fd;
+
+static int init_pfn_mapper(void)
+{
+    if ( (devmem_fd = open("/dev/mem", O_RDWR)) < 0 )
+    {
+        PERROR("Could not open /dev/mem");
+        return -1;
+    }
+    return 0;
+}
+
+static void *map_pfn(unsigned long pfn)
+{
+    void *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
+                       MAP_SHARED, devmem_fd, pfn << PAGE_SHIFT);
+    if ( vaddr == MAP_FAILED )
+    {
+        PERROR("Could not mmap a domain pfn using /dev/mem");
+        return NULL;
+    }
+    return vaddr;
+}
+
+static void unmap_pfn(void *vaddr)
+{
+    (void)munmap(vaddr, PAGE_SIZE);
+}
+
+/*
+ * Returns TRUE if the given machine frame number has a unique mapping
+ * in the guest's pseudophysical map.
+ */
+#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \
+    (((_mfn) < (1024*1024)) &&          \
+     (pfn_to_mfn_table[mfn_to_pfn_table[_mfn]] == (_mfn)))
+
+/* Returns TRUE if MFN is successfully converted to a PFN. */
+static int translate_mfn_to_pfn(unsigned long *pmfn)
+{
+    unsigned long mfn = *pmfn;
+    if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
+        return 0;
+    *pmfn = mfn_to_pfn_table[mfn];
+    return 1;
+}
+
+static int check_pfn_ownership(unsigned long mfn, unsigned int dom)
+{
+    dom0_op_t op;
+    op.cmd = DOM0_GETPAGEFRAMEINFO;
+    op.u.getpageframeinfo.pfn = mfn;
+    if ( (do_dom0_op(&op) < 0) || (op.u.getpageframeinfo.domain != dom) )
+        return 0;
+    return 1;
+}
+
+static unsigned int get_pfn_type(unsigned long mfn)
+{
+    dom0_op_t op;
+    op.cmd = DOM0_GETPAGEFRAMEINFO;
+    op.u.getpageframeinfo.pfn = mfn;
+    if ( do_dom0_op(&op) < 0 )
+    {
+        PERROR("Unexpected failure when getting page frame info!");
+        exit(1);
+    }
+    return op.u.getpageframeinfo.type;
+}
+
+static int checked_write(int fd, const void *buf, size_t count)
+{
+    int rc;
+    while ( ((rc = write(fd, buf, count)) == -1) && (errno = EINTR) )
+        continue;
+    return rc == count;
+}
+
+int main(int argc, char **argv)
+{
+    dom0_op_t op;
+    int rc = 1, i;
+    unsigned long mfn, dom;
+
+    /* Remember if we stopped the guest, so we can restart it on exit. */
+    int we_stopped_it = 0;
+
+    /* A copy of the CPU context of the guest. */
+    full_execution_context_t ctxt;
+
+    /* A copy of the domain's name. */
+    char name[MAX_DOMAIN_NAME];
+
+    /* A table containg the type of each PFN (/not/ MFN!). */
+    unsigned long *pfn_type;
+
+    /* A temporary mapping, and a copy, of one frame of guest memory. */
+    unsigned long *ppage, page[1024];
+
+    /* A temporary mapping, and a copy, of the pfn-to-mfn table frame list. */
+    unsigned long *p_pfn_to_mfn_frame_list, pfn_to_mfn_frame_list[1024];
+    /* A temporary mapping of one frame in the above list. */
+    unsigned long *pfn_to_mfn_frame;
+
+    /* A temporary mapping, and a copy, of the guest's suspend record. */
+    suspend_record_t *p_srec, srec;
+
+    /* The name and descriptor of the file that we are writing to. */
+    char *filename;
+    int fd;
+
+    if ( argv[0] != NULL ) 
+        argv0 = argv[0];
+
+    if ( argc != 3 )
+    {
+        fprintf(stderr, "Usage: %s <domain_id> <state file>\n", argv0);
+        return 1;
+    }
+
+    dom = atoi(argv[1]);
+    if ( dom == 0 )
+    {
+        ERROR("Did you really mean domain 0?");
+        return 1;
+    }
+
+    filename = argv[2];
+    if ( (fd = open(name, O_CREAT|O_EXCL|O_RDWR)) == -1 )
+    {
+        PERROR("Could not open file for writing");
+        return 1;
+    }
+
+    /* Ensure that the domain exists, and that it is stopped. */
+    for ( ; ; )
+    {
+        op.cmd = DOM0_GETDOMAININFO;
+        op.u.getdomaininfo.domain = dom;
+        if ( (do_dom0_op(&op) < 0) || (op.u.getdomaininfo.domain != dom) )
+        {
+            PERROR("Could not get info on domain");
+            goto out;
+        }
+
+        memcpy(&ctxt, &op.u.getdomaininfo.ctxt, sizeof(ctxt));
+        memcpy(name, op.u.getdomaininfo.name, sizeof(name));
+
+        if ( op.u.getdomaininfo.state == DOMSTATE_STOPPED )
+            break;
+
+        we_stopped_it = 1;
+
+        op.cmd = DOM0_STOPDOMAIN;
+        op.u.stopdomain.domain = dom;
+        (void)do_dom0_op(&op);
+
+        sleep(1);
+    }
+
+    /* A cheesy test to see whether the domain contains valid state. */
+    if ( ctxt.pt_base == 0 )
+    {
+        ERROR("Domain is not in a valid Xenolinux state");
+        goto out;
+    }
+
+    if ( init_pfn_mapper() < 0 )
+        goto out;
+
+    /* Is the suspend-record MFN actually valid for this domain? */
+    if ( !check_pfn_ownership(ctxt.i386_ctxt.esi, dom) )
+    {
+        ERROR("Invalid state record pointer");
+        goto out;
+    }
+
+    /* If the suspend-record MFN is okay then grab a copy of it to @srec. */
+    p_srec = map_pfn(ctxt.i386_ctxt.esi);
+    memcpy(&srec, p_srec, sizeof(srec));
+    unmap_pfn(p_srec);
+
+    if ( srec.nr_pfns > 1024*1024 )
+    {
+        ERROR("Invalid state record -- pfn count out of range");
+        goto out;
+    }
+
+    if ( !check_pfn_ownership(srec.pfn_to_mfn_frame_list, dom) )
+    {
+        ERROR("Invalid pfn-to-mfn frame list pointer");
+        goto out;
+    }
+
+    /* Grab a copy of the pfn-to-mfn table frame list. */
+    p_pfn_to_mfn_frame_list = map_pfn(srec.pfn_to_mfn_frame_list);
+    memcpy(pfn_to_mfn_frame_list, p_pfn_to_mfn_frame_list, PAGE_SIZE);
+    unmap_pfn(p_pfn_to_mfn_frame_list);
+
+    /* We want zeroed memory so use calloc rather than malloc. */
+    mfn_to_pfn_table = calloc(1, 4 * 1024 * 1024);
+    pfn_to_mfn_table = calloc(1, 4 * srec.nr_pfns);
+    pfn_type         = calloc(1, 4 * srec.nr_pfns);
+
+    /*
+     * Construct the local pfn-to-mfn and mfn-to-pfn tables. On exit from this
+     * loop we have each MFN mapped at most once. Note that there may be MFNs
+     * that aren't mapped at all: we detect these by MFN_IS_IN_PSEUDOPHYS_MAP.
+     */
+    pfn_to_mfn_frame = NULL;
+    for ( i = 0; i < srec.nr_pfns; i++ )
+    {
+        /* Each frameful of table frames must be checked & mapped on demand. */
+        if ( (i & 1023) == 0 )
+        {
+            mfn = pfn_to_mfn_frame_list[i/1024];
+            if ( !check_pfn_ownership(mfn, dom) )
+            {
+                ERROR("Invalid frame number if pfn-to-mfn frame list");
+                goto out;
+            }
+            if ( pfn_to_mfn_frame != NULL )
+                unmap_pfn(pfn_to_mfn_frame);
+            pfn_to_mfn_frame = map_pfn(mfn);
+        }
+        
+        mfn = pfn_to_mfn_frame[i & 1023];
+
+        if ( !check_pfn_ownership(mfn, dom) )
+        {
+            ERROR("Invalid frame specified with pfn-to-mfn table");
+            goto out;
+        }
+
+        pfn_to_mfn_table[i] = mfn;
+
+        /* Did we map this MFN already? That would be invalid! */
+        if ( MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
+        {
+            ERROR("A machine frame appears twice in pseudophys space");
+            goto out;
+        }
+        
+        mfn_to_pfn_table[mfn] = i;
+
+        /* Query page type by MFN, but store it by PFN. */
+        pfn_type[i] = get_pfn_type(mfn);
+    }
+
+    /* Canonicalise the suspend-record frame number. */
+    if ( !translate_mfn_to_pfn(&ctxt.i386_ctxt.esi) )
+    {
+        ERROR("State record is not in range of pseudophys map");
+        goto out;
+    }
+
+    /* Canonicalise each GDT frame number. */
+    for ( i = 0; i < ctxt.gdt_ents; i += 512 )
+    {
+        if ( !translate_mfn_to_pfn(&ctxt.gdt_frames[i]) )
+        {
+            ERROR("GDT frame is not in range of pseudophys map");
+            goto out;
+        }
+    }
+
+    /* Canonicalise the page table base pointer. */
+    if ( !MFN_IS_IN_PSEUDOPHYS_MAP(ctxt.pt_base >> PAGE_SHIFT) )
+    {
+        ERROR("PT base is not in range of pseudophys map");
+        goto out;
+    }
+    ctxt.pt_base = mfn_to_pfn_table[ctxt.pt_base >> PAGE_SHIFT] << PAGE_SHIFT;
+
+    /* Canonicalise the pfn-to-mfn table frame-number list. */
+    for ( i = 0; i < srec.nr_pfns; i += 1024 )
+    {
+        if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) )
+        {
+            ERROR("Frame # in pfn-to-mfn frame list is not in pseudophys");
+            goto out;
+        }
+    }
+
+    /* Start writing out the saved-domain record. */
+    if ( !checked_write(fd, "XenoLinuxSuspend",    16) ||
+         !checked_write(fd, name,                  sizeof(name)) ||
+         !checked_write(fd, &srec.nr_pfns,         sizeof(unsigned long)) ||
+         !checked_write(fd, &ctxt,                 sizeof(ctxt)) ||
+         !checked_write(fd, pfn_to_mfn_frame_list, PAGE_SIZE) ||
+         !checked_write(fd, pfn_type,              4 * srec.nr_pfns) )
+    {
+        ERROR("Error when writing to state file");
+        goto out;
+    }
+
+    /* Now write out each data page, canonicalising page tables as we go... */
+    for ( i = 0; i < srec.nr_pfns; i++ )
+    {
+        mfn = pfn_to_mfn_table[i];
+
+        ppage = map_pfn(mfn);
+        memcpy(&page, ppage, PAGE_SIZE);
+        unmap_pfn(ppage);
+
+        if ( (pfn_type[i] == L1TAB) || (pfn_type[i] == L2TAB) )
+        {
+            for ( i = 0; i < 1024; i++ )
+            {
+                if ( !(page[i] & _PAGE_PRESENT) ) continue;
+                mfn = page[i] >> PAGE_SHIFT;
+                if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
+                {
+                    ERROR("Frame number in pagetable page is invalid");
+                    goto out;
+                }
+                page[i] &= PAGE_SIZE - 1;
+                page[i] |= mfn_to_pfn_table[mfn] << PAGE_SHIFT;
+            }
+        }
+
+        if ( !checked_write(fd, &page, PAGE_SIZE) )
+        {
+            ERROR("Error when writing to state file");
+            goto out;
+        }
+    }
+
+    /* Success! */
+    rc = 0;
+
+ out:
+    /* Restart the domain if we had to stop it to save its state. */
+    if ( we_stopped_it )
+    {
+        op.cmd = DOM0_STARTDOMAIN;
+        op.u.startdomain.domain = dom;
+        (void)do_dom0_op(&op);
+    }
+
+    /* On error, make sure the file is deleted. */
+    if ( rc != 0 )
+        unlink(filename);
+    
+    return !!rc;
+}
index aad13c56266e93c6bdc4ff915904a8bdd2c5edc1..5c2b47aa0d2c3e5eda224c931cd3713593428baf 100644 (file)
@@ -330,24 +330,34 @@ long do_dom0_op(dom0_op_t *u_dom0_op)
 
     case DOM0_GETPAGEFRAMEINFO:
     {
-        struct pfn_info *page = frame_table + op.u.getpageframeinfo.pfn;
+        struct pfn_info *page;
+        unsigned long pfn = op.u.getpageframeinfo.pfn;
         
-        op.u.getpageframeinfo.domain = page->flags & PG_domain_mask;
-        op.u.getpageframeinfo.type   = NONE;
-        if ( page->type_count & REFCNT_PIN_BIT )
+        if ( pfn >= max_page )
         {
-            switch ( page->flags & PG_type_mask )
+            ret = -EINVAL;
+        }
+        else
+        {
+            page = frame_table + pfn;
+            
+            op.u.getpageframeinfo.domain = page->flags & PG_domain_mask;
+            op.u.getpageframeinfo.type   = NONE;
+            if ( page->type_count & REFCNT_PIN_BIT )
             {
-            case PGT_l1_page_table:
-                op.u.getpageframeinfo.type = L1TAB;
-                break;
-            case PGT_l2_page_table:
-                op.u.getpageframeinfo.type = L2TAB;
-                break;
+                switch ( page->flags & PG_type_mask )
+                {
+                case PGT_l1_page_table:
+                    op.u.getpageframeinfo.type = L1TAB;
+                    break;
+                case PGT_l2_page_table:
+                    op.u.getpageframeinfo.type = L2TAB;
+                    break;
+                }
             }
-        }
 
-        copy_to_user(u_dom0_op, &op, sizeof(op));
+            copy_to_user(u_dom0_op, &op, sizeof(op));
+        }
     }
     break;